# -*- coding: utf-8 -*-
# created on 2017/02/15

# 函数性质综合 - 自动推理
from sympy import sympify, solve
from mathsolver.functions.hanshu.hanshu_inference import (infer_zhouqi, normalize_eq, approch_to_known_values, 
                                                          dairu_eq, get_f_arg, parent_steps)
from mathsolver.functions.base import BaseFunction, BaseFuncName, BasePoly, BaseNumber, new_latex
from mathsolver.functions.hanshu.helper import check_inter, check_func
from mathsolver.functions.sympy_utils import get_all_child
from mathsolver.functions.hanshu.zhouqi import has_f


def approch_to_known_interval(interval, q, zt):
    """根据求出来的周期，把 q 逼近到距离已知区间最近的点
        :param interval 已知区间
        :param q 求 f(q)
        :param zt 周期"""
    end_points = [interval.start, interval.end]
    return approch_to_known_values(end_points, q, zt)


def infer_value(eqs, interval, var, expr, q):
    """根据函数在已知区间上的表达式，推导函数值
    :param eqs a interable of Eqs
    :param interval 已知区间
    :param expr 函数在已知区间上的表达式
    :param var 变量
    :param q 计算 f(q)"""

    # 如果 q 的值已知，直接输出 f(q)
    if q in interval:
        final_result = expr.subs(var, q)
        step = "因为 %s ∈ %s 所以 f(%s) = %s" % (new_latex(q), new_latex(interval), new_latex(q), new_latex(final_result))
        return final_result, step

    # 根据给定条件推导 f(q) 的值
    fq = sympify('f(%s)' % q)
    infer_stack = [fq]
    i = -1
    while i < 10:
        i += 1
        to_dairu_indices = range(2 ** i - 1, 2 * (2 ** i - 1) + 1, 1)
        to_dairu = [infer_stack[ii] for ii in to_dairu_indices]
        for f_item in to_dairu:
            for eq in eqs:
                intermediate_result = dairu_eq(f_item, eq)

                # 如果迭代得到的值已知，停止迭代，返回结果
                f_arg = get_f_arg(intermediate_result)
                if f_arg in interval:
                    # 最终结果， intermediate_result 为 f(a) 或者 -f(a)
                    final_result = intermediate_result.subs(sympify('f(%s)' % f_arg), expr.subs(var, f_arg))

                    # 推导步骤
                    all_intermediates = parent_steps(infer_stack)
                    all_intermediates.extend([intermediate_result, final_result])
                    step = ' = '.join(new_latex(item) for item in all_intermediates)

                    return final_result, step
                else:
                    # 尝试解方程
                    solve_eq = solve(fq - intermediate_result, fq)
                    # 如果解出来则迭代结束，返回结果
                    if solve_eq and (solve_eq[0].is_Number or isinstance(solve_eq[0], int)):
                        final_result = solve_eq[0]
                        all_intermediates = parent_steps(infer_stack)
                        all_intermediates.append(intermediate_result)
                        step = ' = '.join(str(item) for item in all_intermediates) + "解方程得 %s = %s" % (
                            new_latex(fq), new_latex(final_result))

                        return final_result, step
                    else:
                        # 如果推导不出来结果，把中间值放到 stack 上面，继续推导
                        infer_stack.append(intermediate_result)

    if i == 10:
        raise ValueError('迭代超过十次、未找出问题解')


def solve_hanshu_qiuzhi_2(eqs, interval, var, expr, q):
    """函数性质综合求函数的值——函数解析式
    :param eqs: a iterable of Eqs
    :param interval: 已知区间
    :param var: 函数表达式 var -> expr
    :param expr 函数在已知区间上的表达式
    :param q: calculate f(q)"""

    # 把 eqs 都转换成 f(x) = ... 的形式
    eqs = [normalize_eq(item) for item in eqs]

    # 从 eqs 推导周期
    zt, zhouqi_steps = infer_zhouqi(*eqs)

    # 逼近到已知区间
    if zhouqi_steps:
        q1, approx_step = approch_to_known_interval(interval, q, zt)
        zhouqi_steps.append(["", approx_step])
    else:
        q1 = q

    result, inference_step = infer_value(eqs, interval, var, expr, q1)

    return result, zhouqi_steps + [["", inference_step]] + [["", "综上所述 f(%s) = %s" % (new_latex(q), new_latex(result))]]


class HanShuQiuZhi2(BaseFunction):
    """ 奇偶，周期 + 区间 + 表达式 + 求值 """
    def solver(self, *args):
        eq_list, inter, func, funcname_poly = args
        inter, func = check_inter(inter), check_func(func)
        var, expr = func.var, func.expression

        # 问题可能是 BaseFuncName 和 BasePoly 两种形式
        # 求 f(a)
        if isinstance(funcname_poly, BaseFuncName):
            f_q = funcname_poly.var
            result, steps = solve_hanshu_qiuzhi_2(eq_list, inter, var, expr, f_q)
            self.steps.extend(steps)
            self.output.append(BaseNumber(result))
        # 求 f(a) + f(b)
        elif isinstance(funcname_poly, BasePoly):
            poly = sympify(funcname_poly.value)
            fs = get_all_child(poly, has_f)
            fs_solve = {}
            for f in fs:
                f_q = f.args[0]
                f_result, f_steps = solve_hanshu_qiuzhi_2(eq_list, inter, var, expr, f_q)
                fs_solve[f] = f_result
                self.steps.extend(f_steps)

            final_res = poly.subs(fs_solve)
            self.steps.append(["", "所以 %s = %s" % (new_latex(poly), new_latex(final_res))])
            self.output.append(BaseNumber(final_res))

        self.label.add("函数性质综合求函数的值——函数值")
        return self


if __name__ == '__main__':
    pass
